Skip to content

fix(teardown): prevent descriptor exhaustion during Treehouse return - #26

Open
ruby-dlee wants to merge 2 commits into
mainfrom
fm/fm-reap-fdcap-r7
Open

fix(teardown): prevent descriptor exhaustion during Treehouse return#26
ruby-dlee wants to merge 2 commits into
mainfrom
fm/fm-reap-fdcap-r7

Conversation

@ruby-dlee

@ruby-dlee ruby-dlee commented Jul 25, 2026

Copy link
Copy Markdown
Owner

Intent

Prevent production teardown failures on realistically sized repositories by capping the directory descriptors inherited by treehouse return --force.

Relvino worktrees contain about 41,884 directories.
The previous walk retained one descriptor per directory and made every descriptor inheritable across exec, which caused Treehouse and its Git child to fail with Too many open files.

What changed

  • Validate each descendant directory with the existing descriptor-relative checks, then close its descriptor instead of retaining it.
  • Keep the validated root descriptor open and inheritable so os.fchdir continues to bind Treehouse to the intended worktree.
  • Preserve the symlinked-ancestor, non-directory, project/cwd, dev/ino round-trip, single-device, mount-point, and O_NOFOLLOW checks.
  • Add a real generated fixture with more than 42,000 directories and a depth beyond the imposed descriptor limit.
  • Reconcile the teardown pathname-swap test with the guarantee the design actually retains.

Security boundary

The retained root descriptor still binds Treehouse to the validated worktree if the root pathname is replaced with a symlink before Treehouse acts.

Descendant validation is now explicitly point-in-time.
Descendant descriptors are not retained across exec, so the previous test's stronger claim that every descendant identity remained bound during a mid-return swap no longer holds.
The test now pins the surviving root-binding guarantee instead of silently claiming otherwise.

Deliberate non-goal

The pre-existing failed-spawn rollback failure on origin/main is not addressed here and is tracked separately as fm-rollback-lock-assertion-red.
That rollback path currently fails before Treehouse is involved.
The pre-existing full-repository Lint and Behavior reds are also not addressed here and are tracked separately as fm-main-green-g7.
This capacity-only PR does not change tests/fm-account-routing.test.sh, the rollback implementation, or any of the files reported by the baseline lint failure.
It must not be read as fixing the separately tracked lock-owner assertion or main's unrelated lint and behavior failures.

Verification

  • bash tests/fm-checkout-return-boundary.test.sh
    • All existing refusal cases passed.
    • The generated greater-than-42,000-directory deep fixture completed under a 128-descriptor limit.
    • The exec'd fake Treehouse observed only the retained root directory descriptor.
  • FM_TEST_FOCUSED=treehouse-return-root-swap bash tests/fm-teardown.test.sh
  • FM_TEST_FOCUSED=treehouse-return-directory-capacity bash tests/fm-teardown.test.sh
  • bash -n bin/fm-checkout-lock-lib.sh tests/fm-checkout-return-boundary.test.sh tests/fm-teardown.test.sh
  • git diff --check

The local machine does not currently have the repository's pinned ShellCheck 0.11.0 binary, so the local lint command reported only that missing-tool condition.
CI installs the pinned binary before running bin/fm-lint.sh -x and is the authoritative lint result.

Pipeline provenance

Updates from git push no-mistakes

No-mistakes run 01KYDAETNSKB9ADWAG322ZFKY4 completed intent, rebase, and push for this exact capacity-only head with outcome passed.
Review, test, document, lint, PR, and CI were deliberately skipped in that provenance-only run because the capacity checks are recorded above and the separately assigned fm-main-green-g7 owns the pre-existing full-repository reds.

@ruby-dlee
ruby-dlee force-pushed the fm/fm-reap-fdcap-r7 branch 2 times, most recently from a04ae44 to df082bc Compare July 25, 2026 18:22
@ruby-dlee
ruby-dlee force-pushed the fm/fm-reap-fdcap-r7 branch from df082bc to eb24f90 Compare July 25, 2026 19:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant